#include "Cmaid_Gtest.h"
#include <map>

TEST_P(Cmaid_Set_Gtest, count_add)
{
	int i;

	for (i = 0; i < dataCount(); i++)
	{
		ASSERT_EQ(i, cmaid_container_count(set));
		cmaid_set_add(set, dataAt(i));
	}
}

TEST_P(Cmaid_Set_Gtest, count_add_same)
{
	int i;

	ASSERT_EQ(0, cmaid_container_count(set));
	cmaid_set_add(set, dataAt(0));

	for (i = 0; i < 1000; i++)
	{
		ASSERT_EQ(1, cmaid_container_count(set));
		cmaid_set_add(set, dataAt(0));
	}
}

TEST_P(Cmaid_Set_Gtest, add)
{
	int i;

	for (i = 0; i < dataCount(); i++)
	{
		void *data;
		cmaid_set_add(set, dataAt(i));

		data = cmaid_set_get(set, dataAt(i));
		/* if there is no data return fail */
		ASSERT_TRUE(NULL != data);
		ASSERT_DATA_EQ(dataAt(i), data);
		if (isPointerType())
			ASSERT_EQ(dataAt(i), data);
		else
			ASSERT_NE(dataAt(i), data);
	}
}

TEST_P(Cmaid_Set_Gtest, remove)
{
	int i;

	for (i = 0; i < dataCount(); i++)
		cmaid_set_add(set, dataAt(i));

	ASSERT_EQ(dataCount(), cmaid_container_count(set));

	for (i = 0; i < dataCount(); i++)
	{
		void *name = cmaid_set_get(set, dataAt(i));

		ASSERT_DATA_EQ(dataAt(i), name);
		cmaid_set_remove(set, name);
		ASSERT_EQ(dataCount() - i - 1, cmaid_container_count(set));
		/* this name shouldn't be in the set anymore */
		cmaid_set_remove(set, dataAt(i));
		/* ergo the container count should be the same as before */
		ASSERT_EQ(dataCount() - i - 1, cmaid_container_count(set));
	}
}

TEST_P(Cmaid_Set_Gtest, remove_inverse)
{
	int i;

	for (i = 0; i < dataCount(); i++)
		cmaid_set_add(set, dataAt(i));
	
	for (i = dataCount() - 1; i >= 0; i--)
	{
		void *name = cmaid_set_get(set, dataAt(i));

		ASSERT_DATA_EQ(dataAt(i), name);
		cmaid_set_remove(set, name);
		ASSERT_EQ(i, cmaid_container_count(set));
		/* this name shouldn't be in the set anymore */
		cmaid_set_remove(set, dataAt(i));
		/* ergo the container count should be the same as before */
		ASSERT_EQ(i, cmaid_container_count(set));
	}
}

TEST_P(Cmaid_Set_Gtest, clear)
{
	int i;

	/* first clear an empty set, I know that seems to be non-sense
	 * but maybe we trigger a hidden bug */
	cmaid_container_clear(set);
	ASSERT_EQ(0, cmaid_container_count(set));

	for (i = 0; i < dataCount(); i++)
		cmaid_set_add(set, dataAt(i));
	
	/* and now do the same with a filled set */
	cmaid_container_clear(set);
	ASSERT_EQ(0, cmaid_container_count(set));

	/* let's see if we find a name in it */
	for (i = 0; i < dataCount(); i++)
	{
		void *data;
		data = cmaid_set_get(set, dataAt(i));
		ASSERT_TRUE(NULL == data);
	}
}

TEST_P(Cmaid_Set_Gtest, empty)
{
	int i;

	ASSERT_EQ(0, cmaid_container_count(set));
	/* let's see if we find a name */
	for (i = 0; i < dataCount(); i++)
	{
		void *data;
		data = cmaid_set_get(set, dataAt(i));
		ASSERT_TRUE(NULL == data);
	}
}

TEST_P(Cmaid_Set_Gtest, dump)
{
	Cmaid_Iter iter __CMAID_CHECKED_ITER__;
	FILE *file;
	void *data;
	int size;

	/* Of course we cannot test the output of dump here without
	 * writing an disproportional difficult parser. So we only test
	 * it to see if it segfault and do some alibi tests */
	fill();
	cmaid_container_iter_attach(set, &iter);
	data = cmaid_iter_index_goto(&iter, 3);
	size = cmaid_container_count(set);

	file = tmpfile();
	cmaid_container_dump(set, file);
	fclose(file);

	ASSERT_EQ(size, cmaid_container_count(set));
	ASSERT_DATA_EQ(data, cmaid_iter_current(&iter));
	ASSERT_EQ(3, cmaid_iter_index_get(&iter));
	
	cmaid_container_iter_detach(set, &iter);
}

TEST_P(Cmaid_Set_Gtest, count_add2)
{
	int i;

	for (i = 0; i < dataCount(); i++)
	{
		void *ret;
		ASSERT_EQ(cmaid_container_count(set), i);
		ret = cmaid_set_add(set, dataAt(i));
		ASSERT_TRUE(NULL != ret) << "No data added! i == " << i;
	}
}

TEST_P(Cmaid_Set_Gtest, count_add_same2)
{
	int i;

	ASSERT_EQ(0, cmaid_container_count(set));
	cmaid_set_add(set, dataAt(0));

	for (i = 0; i < dataCount(); i++)
	{
		ASSERT_EQ(1, cmaid_container_count(set));
		cmaid_set_add(set, dataAt(0));
	}
}

TEST_P(Cmaid_Set_Gtest, add2)
{
	int i;

	for (i = 0; i < dataCount(); i++)
	{
		void *data;

		cmaid_set_add(set, dataAt(i));

		data = cmaid_set_get(set, dataAt(i));
		/* if there is no data return fail */
		ASSERT_TRUE(NULL != data);
		ASSERT_DATA_EQ(dataAt(i), data);
	}
}

TEST_P(Cmaid_Set_Gtest, remove2)
{
	int i;

	for (i = 0; i < dataCount(); i++)
		cmaid_set_add(set, dataAt(i));
	
	for (i = 0; i < dataCount(); i++)
	{
		void *ret = cmaid_set_get(set, dataAt(i));
		ASSERT_TRUE(NULL != ret);
		ASSERT_DATA_EQ(dataAt(i), ret);
		cmaid_set_remove(set, ret);
		ASSERT_EQ(dataCount() - cmaid_container_count(set) - 1, i);
		/* this name shouldn't be in the set anymore */
		cmaid_set_remove(set, dataAt(i));
		/* ergo the container count should be the same as before */
		ASSERT_EQ(dataCount() - cmaid_container_count(set) - 1, i);
	}
}

TEST_P(Cmaid_Set_Gtest, check_massive_set_remove_inverse)
{
	int i;

	for (i = 0; i < dataCount(); i++)
		cmaid_set_add(set, dataAt(i));
	
	for (i = dataCount() - 1; i >= 0; i--)
	{
		void *ret = cmaid_set_get(set, dataAt(i));
		ASSERT_TRUE(NULL != ret);
		ASSERT_DATA_EQ(dataAt(i), ret);
		cmaid_set_remove(set, ret);
		ASSERT_EQ(cmaid_container_count(set), i);
		/* this name shouldn't be in the set anymore */
		cmaid_set_remove(set, dataAt(i));
		/* ergo the container count should be the same as before */
		ASSERT_EQ(cmaid_container_count(set), i);
	}
}

TEST_P(Cmaid_Set_Gtest, clear2)
{
	int i;

	for (i = 0; i < dataCount(); i++)
		cmaid_set_add(set, dataAt(i));
	
	/* and now do the same with a filled set */
	cmaid_container_clear(set);
	ASSERT_EQ(0, cmaid_container_count(set));

	/* let's see if we find a name in it */
	for (i = 0; i < dataCount(); i++)
	{
		void *data;
		data = cmaid_set_get(set, dataAt(i));
		ASSERT_TRUE(NULL == data);
	}
}

TEST_P(Cmaid_Set_Gtest, check_massive_set_iter_next)
{
	int i;
	Cmaid_Iter it;
	void *ret;
	std::map<void *, int> map;
	std::map<void *, int>::const_iterator map_it, end;

	for (i = 0; i < dataCount(); i++)
		cmaid_set_add(set, dataAt(i));

	cmaid_container_iter_attach(set, &it);
	while ((ret = cmaid_iter_next(&it)))
		map[ret] += 1;

	/* check if every element was found and only one */
	end = map.end();
	for (map_it = map.begin(); map_it != end; map_it++)
		ASSERT_EQ(1, map_it->second);
	
	cmaid_container_iter_detach(set, &it);
}

TEST_P(Cmaid_Set_Gtest, iter_previous)
{
	int i;
	Cmaid_Iter it;
	void *ret;
	std::map<void *, int> map;
	std::map<void *, int>::const_iterator map_it, end;

	for (i = 0; i < dataCount(); i++)
		cmaid_set_add(set, dataAt(i));

	cmaid_container_iter_attach(set, &it);
	while ((ret = cmaid_iter_previous(&it)))
		map[ret]++;

	/* check if every number was found and only one */
	end = map.end();
	for (map_it = map.begin(); map_it != end; map_it++)
		ASSERT_EQ(1, map_it->second);
	
	cmaid_container_iter_detach(set, &it);
}
#if 0
TEST_P(Cmaid_Set_Gtest, iter_index_goto)
{
	int i;
	Cmaid_Set *s = CURRENT_SET();
	Cmaid_Iter it;
	int *ret;
	int *numbers = calloc(MASSIVE_NUM, sizeof(int));

	for (i = 0; i < MASSIVE_NUM; i++)
		cmaid_set_add(s, &i);

	i = 0;
	cmaid_container_iter_attach(s, &it);
	while ((ret = cmaid_iter_next(&it)))
		numbers[i++] = *ret;

	for (i = 0; i < MASSIVE_NUM; i++)
	{
		const int j = MASSIVE_NUM - i - 1;

		ret = cmaid_iter_index_goto(&it, i);
		fail_if(!ret);
		fail_if(*ret != numbers[i]);
		fail_if(cmaid_iter_index_get(&it) != i);
		
		cmaid_iter_index_goto(&it, j);
		ret = cmaid_iter_current(&it);
		fail_if(!ret);
		fail_if(*ret != numbers[j]);
		fail_if(cmaid_iter_index_get(&it) != j);
	}

	free(numbers);
	cmaid_container_iter_detach(s, &it);
}
#endif

TEST_P(Cmaid_Set_Gtest, check_massive_set_iter_iter_goto)
{
	int index;

	for (index = 0; index < dataCount(); index++)
	{
		Cmaid_Iter it1, it2;
		int i;
		void *ret;
		
		fill();

		cmaid_container_iter_attach(set, &it1);
		cmaid_container_iter_attach(set, &it2);

		/* Go now with the first iterator to the _i-th node */
		cmaid_iter_index_goto(&it1, index);
		/* let's see if the other iter will follow */
		ret = cmaid_iter_iter_goto(&it2, &it1);
		ASSERT_TRUE(NULL != ret);
		
		ASSERT_TRUE(NULL != cmaid_iter_current(&it2));
		ASSERT_EQ(cmaid_iter_current(&it2), cmaid_iter_current(&it1));
		ASSERT_EQ(cmaid_iter_index_get(&it2), cmaid_iter_index_get(&it1));
		
		cmaid_container_iter_detach(set, &it1);
		cmaid_container_iter_detach(set, &it2);

		cmaid_container_clear(set);
	}
}

TEST_P(Cmaid_Set_Gtest, iter_remove)
{
	Cmaid_Iter it;
	int i;
	void *ret;

	fill();	

	cmaid_container_iter_attach(set, &it);
	/* remove every odd number */
	while ((ret = cmaid_iter_next(&it)))
		cmaid_iter_remove(&it);

	i = 0;
	/* and now check if we really removed all */
	while ((ret = cmaid_iter_next(&it)))
		i++;

	ASSERT_EQ(0, i);
	ASSERT_EQ(0, cmaid_container_count(set));
	cmaid_container_iter_detach(set, &it);
}

TEST_P(Cmaid_Set_Gtest, iter_remove2)
{
	int index;

	for (index = 0; index < dataCount() - 2; index++)
	{
		Cmaid_Iter it, it_pre, it_post;
		int i;
		
		fill();

		cmaid_container_iter_attach(set, &it_pre);
		cmaid_iter_index_goto(&it_pre, index);

		cmaid_container_iter_attach(set, &it);
		cmaid_iter_iter_goto(&it, &it_pre);
		cmaid_iter_next(&it);

		cmaid_container_iter_attach(set, &it_post);
		cmaid_iter_iter_goto(&it_post, &it);
		cmaid_iter_next(&it_post);

		cmaid_iter_remove(&it);

		ASSERT_EQ(cmaid_iter_index_get(&it_pre),
				cmaid_iter_index_get(&it));
		ASSERT_EQ(cmaid_iter_current(&it_pre),
				cmaid_iter_current(&it));
		ASSERT_EQ(index + 1, cmaid_iter_index_get(&it_post));

		cmaid_container_iter_detach(set, &it);
		cmaid_container_iter_detach(set, &it_post);
		cmaid_container_iter_detach(set, &it_pre);

		cmaid_container_clear(set);
	}
}

TEST_P(Cmaid_Set_Gtest, iter_remove3)
{
	int index;

	for (index = 0; index < dataCount() - 2; index++)
	{
		Cmaid_Iter it, it_pre, it_post;
		int i;

		fill();		

		cmaid_container_iter_attach(set, &it_pre);
		cmaid_iter_index_goto(&it_pre, index);

		cmaid_container_iter_attach(set, &it);
		cmaid_iter_iter_goto(&it, &it_pre);
		cmaid_iter_next(&it);

		cmaid_container_iter_attach(set, &it_post);
		cmaid_iter_iter_goto(&it_post, &it);
		cmaid_iter_next(&it_post);

		cmaid_set_remove(set, cmaid_iter_current(&it));
		ASSERT_EQ(cmaid_iter_index_get(&it_pre), cmaid_iter_index_get(&it));
		ASSERT_EQ(cmaid_iter_current(&it_pre), cmaid_iter_current(&it));
		ASSERT_EQ(index + 1, cmaid_iter_index_get(&it_post))
			<< "index == " << index;

		cmaid_container_iter_detach(set, &it);
		cmaid_container_iter_detach(set, &it_post);
		cmaid_container_iter_detach(set, &it_pre);

		cmaid_container_clear(set);
	}
}

static int
validate_iter_index(Cmaid_Iter *it)
{
	Cmaid_Iter iter;
	/* we are testing a cmaid set, hence we have a cmaid container,
	 * so the cast is ok */
	Cmaid_Container *c = (Cmaid_Container *)cmaid_iter_container_get(it);
	int index;

	cmaid_container_iter_attach(c, &iter);
	cmaid_iter_iter_goto(&iter, it);
	index = cmaid_iter_index_get(it);

	while (cmaid_iter_next(&iter))
		index++;

	cmaid_container_iter_detach(c, &iter);

	return cmaid_container_count(c) - 1 == index;
}

TEST_P(Cmaid_Set_Gtest, iter_add)
{
#define NUMBER_OF_ITER 15
	Cmaid_Iter it[NUMBER_OF_ITER];
	int i;
	
	for (i = NUMBER_OF_ITER; i < (NUMBER_OF_ITER * 2); i++)
		cmaid_set_add(set, dataAt(i));

	/* now attach the iters */
	for (i = 0; i < NUMBER_OF_ITER; i++)
	{
		cmaid_container_iter_attach(set, &it[i]);
		cmaid_iter_index_goto(&it[i], i);
	}

	/* validate the position after the goto */
	for (i = 0; i < NUMBER_OF_ITER; i++)
		ASSERT_TRUE(validate_iter_index(&it[i]));

	/* append the rest of the stuff, we also add the already added
	 * numbers this must not make a difference and simplifies the loop */
	for (i = 0; i < dataCount(); i++)
		cmaid_set_add(set, dataAt(i));

	/* validate the position after the adds */
	for (i = 0; i < NUMBER_OF_ITER; i++)
		ASSERT_TRUE(validate_iter_index(&it[i]));

	/* now detach the iters */
	for (i = 0; i < NUMBER_OF_ITER; i++)
		cmaid_container_iter_detach(set, &it[i]);
#undef NUMBER_OF_ITER
}

